package org.gap.wizards.world.builder;

import java.awt.Dimension;
import java.awt.Point;
import java.util.Random;

import java.util.List;
import java.util.ArrayList;

public class MapBase {
	private Random random = new Random();
	protected char[][] theMap;
	private List<Wall> walls = new ArrayList<Wall>();
	
	protected class Wall {
		private Point location;
		private Direction facing;
		public Wall(Point start, Direction side) {
			location = start;
			facing = side;
		}
		
		public Point start() { return location; }
		public Direction facing() { return facing; }
	}
	
	public void initialize() {
		walls.clear();
		theMap = null;
	}
	
	protected void fillMap(int width, int height) {
		theMap = new char[height][width];
		for (int i = 0; i < theMap.length; i++)
			for (int j = 0; j < theMap[i].length; j++)
				theMap[i][j] = ' ';
	}
	
	/**
	 * Randomly creates a new point.
	 * @return
	 */
	protected Point generateLocation(int width, int height) {
		int y = height - 10;
		int x = width - 10;
		y = random.nextInt(y) + 5;
		x = random.nextInt(x) + 5;
		return new Point(x, y);
	}
	
	protected int generateLength() {
		int length = theMap[0].length / 3;
		return next(length) + 2;
	}
	
	/**
	 * Randomly gets the next wall in the collection.
	 * @return
	 */
	protected Wall generateWall() {
		if (walls.isEmpty() || count > walls.size() * walls.size())
			return null;
		int i = next(walls.size());
		Wall wall = walls.get(i);
		count++;
		return wall;
	}
	private int count = 0;
	protected void usedWall(Wall wall) {
		count = 0;
		if (wall == null)
			return;
		walls.remove(walls.indexOf(wall));
	}
	
	/**
	 * Randomly creates a new dimension.
	 * @return
	 */
	protected Dimension generateRoom() {
		int width = random.nextInt(3) + 3;
		int height = random.nextInt(3) + 3;
		return new Dimension(width, height);
	}
	
	/**
	 * returns a randomly generated direction of either north, south, east or west.
	 * @return
	 */
	protected Direction generateDirection() {
		switch (random.nextInt(4)) {
			case 0: return Direction.NORTH;
			case 1: return Direction.SOUTH;
			case 2: return Direction.EAST;
			default : return Direction.WEST;
		}
	}
	
	/**
	 * Checks if a given length of walk space is clear to be used, returns false if it touches or runs into
	 * an already made space.
	 * @param start
	 * @param length
	 * @param dir
	 * @return
	 */
	protected boolean check(Point start, int length, Direction dir) {
		switch(dir) 
		{
			case NORTH:
				return isClear(start.x, start.y - length, 1, length);
			case SOUTH:
				return isClear(start.x, start.y, 1, length);
			case EAST:
				return isClear(start.x, start.y, length, 1);
			case WEST:
				return isClear(start.x - length, start.y, length, 1);
		}
		return false;
	}
	
	/**
	 * Checks if the given space can accomodate a room of the given dimensions.
	 * @param start
	 * @param dimensions
	 * @param dir
	 * @return
	 */
	protected boolean check(Point start, Dimension dimensions, Direction dir) {
		switch(dir) 
		{
			case NORTH:
				return isClear(start.x - dimensions.width / 2, start.y - dimensions.height - 1, dimensions.width, dimensions.height + 1);
			case SOUTH:
				return isClear(start.x - dimensions.width / 2, start.y, dimensions.width, dimensions.height + 1);
			case EAST:
				return isClear(start.x, start.y - dimensions.height / 2, dimensions.width + 1, dimensions.height);
			case WEST:
				return isClear(start.x - dimensions.width - 1, start.y - dimensions.height / 2, dimensions.width + 1, dimensions.height);
		}
		return false;
	}
	
	private boolean isClear(int x, int y, int width, int height) {
		if (x < 0 || y < 0)
			return false;
		
		for (int i = y; i <= y + height; i++) {
			if (i >= theMap.length) 
				return false;
			for (int j = x; j <= x + width; j++)
				if (j >= theMap[0].length)
					return false;
				else if (theMap[i][j] != ' ')
					return false;
		}
		return true;
	}
	
	/**
	 * Draws the path in the assumed cleared out space.
	 * @param start
	 * @param length
	 * @param dir
	 */
	protected void draw(Point start, int length, Direction dir) {
		System.out.println("draw hall: " + dir);
		switch(dir) 
		{
			case NORTH:
				drawHall(start.x, start.y - length, length, true);
				if (length != 1) {
					walls.add(new Wall(new Point(start.x - 1, start.y + next(length) - length), Direction.WEST));
					walls.add(new Wall(new Point(start.x + 1, start.y + next(length) - length + 1), Direction.EAST));
				}
				if (start.y - length - 1 > 0)
					walls.add(new Wall(new Point(start.x, start.y - length - 1), dir));
				break;
			case SOUTH:
				drawHall(start.x, start.y, length, true);
				if (length != 1) {
					walls.add(new Wall(new Point(start.x - 1, start.y + next(length)), Direction.WEST));
					walls.add(new Wall(new Point(start.x + 1, start.y + next(length)), Direction.EAST));
				}
				if (start.y + length < theMap.length - 1)
					walls.add(new Wall(new Point(start.x, start.y + length + 1), dir));
				break;
			case EAST:
				drawHall(start.x, start.y, length, false);
				if (length != 1)
				{
					walls.add(new Wall(new Point(start.x + next(length), start.y - 1), Direction.NORTH));
					walls.add(new Wall(new Point(start.x + next(length), start.y + 1), Direction.SOUTH));
				}
				if (start.x + length < theMap[0].length)
					walls.add(new Wall(new Point(start.x + length + 1, start.y), dir));				
				break;
			case WEST:
				drawHall(start.x - length, start.y, length, false);
				if (length != 1)
				{
					walls.add(new Wall(new Point(start.x + next(length) - length, start.y - 1), Direction.NORTH));
					walls.add(new Wall(new Point(start.x + next(length) - length, start.y + 1), Direction.SOUTH));
				}
				if (start.x - length - 1 > 0)
					walls.add(new Wall(new Point(start.x - length - 1, start.y), dir));
				break;
		}
	}
	
	protected void draw(Point start, Dimension dimensions, Direction dir) {
		System.out.println("draw room: " + dir);
		switch(dir) 
		{
			case NORTH:
				drawRoom(start.x - dimensions.width / 2, start.y - dimensions.height, dimensions.width, dimensions.height, start );
				break;
			case SOUTH:
				drawRoom(start.x - dimensions.width / 2, start.y, dimensions.width, dimensions.height, start);
				break;
			case EAST:
				drawRoom(start.x , start.y - dimensions.height / 2, dimensions.width, dimensions.height, start);
				break;
			case WEST:
				drawRoom(start.x - dimensions.width, start.y - dimensions.height / 2, dimensions.width, dimensions.height, start);
				break;
		}
	}
	
	private int next(int range) {
		if (range <= 1)
			return 0;
		return random.nextInt(range);
	}
	
	private void drawHall(int x, int y, int length, boolean isVertical) {
		if (isVertical) 
			for (int i = y; i <= y + length; i++)
				if (theMap[i][x] == ' ' || theMap[i][x] == '.') 
					theMap[i][x] = '#';					
				else {
					i--;
					x++;
				}
			
		else
			for (int i = x; i <= x + length; i++)
				if (theMap[y][i] == ' ' || theMap[y][i] == '.')
					theMap[y][i] = '#';
				else {
					i--;
					y++;
					theMap[y][i] = '#';
				}
	}
	
	private void drawRoom(int x, int y, int width, int height, Point entrance) {
		for (int i = y; i <= y + height; i++)
			for (int j = x; j <= x + width; j++)
			{	
				char draw = '.';
				if (i == y || i == y + height)
					draw = '-';
				else if (j == x  || j == x + width)
					draw = '|';
				theMap[i][j] = draw;
			}
		
		if (next(10) % 9 == 0)
			theMap[entrance.y][entrance.x] = 's';
		else
			theMap[entrance.y][entrance.x] = '+';
	}
	
	protected void drawMap() {
		for (int i = 0; i < theMap.length; i++) {
			for (int j = 0; j < theMap[i].length; j++)
				System.out.print(theMap[i][j]);
			System.out.println();
		}
	}
}
